"""应用级配置常量与默认值。

该模块集中管理 MQTT 连接默认参数、用户自定义配置的存储位置以及 JSON 数据文件路径。
界面层会读取默认值，并通过提供的工具函数加载/保存用户修改后的 MQTT 配置。
"""

from __future__ import annotations

import json
import logging
import sys
from dataclasses import dataclass
from pathlib import Path


LOGGER = logging.getLogger(__name__)

# 确定项目根目录
# 如果是在 PyInstaller 打包后的环境中运行，使用 exe 所在目录
# 否则使用源代码目录
if getattr(sys, 'frozen', False):
    # PyInstaller 打包后的环境
    PROJECT_ROOT = Path(sys.executable).parent
else:
    # 开发环境
    PROJECT_ROOT = Path(__file__).resolve().parent.parent

USER_CONFIG_PATH = PROJECT_ROOT / "user_config.json"


@dataclass(frozen=True)
class MQTTDefaults:
    host: str = "broker.emqx.io"
    port: int = 1883
    username: str | None = ""
    password: str | None = ""


@dataclass(frozen=True)
class DeviceDefaults:
    product_key: str = "your_product_key_example"
    device_sn: str = "your_device_sn_001"
    device_secret: str = "your_device_secret_xyz"


@dataclass(frozen=True)
class PublishDefaults:
    """随机发布默认配置。"""
    interval_seconds: int = 2  # 随机发布时间间隔（秒）


@dataclass(frozen=True)
class TopicTemplateDefaults:
    """Topic模板默认配置。"""
    template: str = "v1/{product_key}/{device_sn}/sys/property/up"  # Topic模板，支持{product_key}和{device_sn}占位符


@dataclass(frozen=True)
class MessageTemplateDefaults:
    """消息模板默认配置。"""
    template: str = '{"msgid": "{msgid}", "params": {params}}'  # 消息模板，支持{msgid}和{params}占位符


@dataclass(frozen=True)
class DatabaseDefaults:
    """数据库连接默认配置。"""
    host: str = "172.16.0.105"
    port: int = 3307
    database: str = "edgej-device"
    user: str = ""
    password: str = ""


MQTT_DEFAULTS = MQTTDefaults()
DEVICE_DEFAULTS = DeviceDefaults()
PUBLISH_DEFAULTS = PublishDefaults()
DB_DEFAULTS = DatabaseDefaults()
TOPIC_TEMPLATE_DEFAULTS = TopicTemplateDefaults()
MESSAGE_TEMPLATE_DEFAULTS = MessageTemplateDefaults()

# 物模型名称映射表
# 将parent_id映射到对应的系统名称
IOT_MODEL_NAME_MAPPING: dict[int, str] = {
    1: "车站数字化运行系统",
    2: "环控",
    3: "给排水",
    4: "消防水",
    5: "动力照明",
    6: "站内客运设备",
    7: "站台门",
    8: "火灾报警系统",
    9: "门禁系统",
    10: "自动售检票系统",
    11: "车站附属建筑",
}


def get_iot_model_name(parent_id: int | None, default_name: str | None = None) -> str:
    """根据parent_id获取物模型名称。
    
    Args:
        parent_id: 物模型的parent_id
        default_name: 如果映射表中不存在，则返回的默认名称
        
    Returns:
        映射后的物模型名称，如果不存在则返回default_name或原始名称
    """
    if parent_id is None:
        return default_name or "未知分类"
    
    return IOT_MODEL_NAME_MAPPING.get(parent_id, default_name or f"物模型 {parent_id}")


def _load_user_config() -> dict:
    """加载统一的用户配置文件。
    
    Returns:
        配置字典，包含 mqtt、database、topic_template 和 message_template 等键
    """
    if not USER_CONFIG_PATH.exists():
        return {}
    
    try:
        data = json.loads(USER_CONFIG_PATH.read_text(encoding="utf-8"))
        return data if isinstance(data, dict) else {}
    except Exception:  # noqa: BLE001
        LOGGER.exception("加载用户配置失败")
        return {}


def _save_user_config(config: dict) -> None:
    """保存统一的用户配置文件。
    
    Args:
        config: 配置字典，应包含 mqtt、database、topic_template 和 message_template 等键
    """
    try:
        USER_CONFIG_PATH.write_text(
            json.dumps(config, ensure_ascii=False, indent=2),
            encoding="utf-8",
        )
    except Exception:  # noqa: BLE001
        LOGGER.exception("保存用户配置失败")


def has_mqtt_user_config() -> bool:
    """检查是否存在用户保存的 MQTT 配置。
    
    Returns:
        如果配置文件存在且包含MQTT配置，返回 True；否则返回 False
    """
    config = _load_user_config()
    return "mqtt" in config and bool(config["mqtt"])


def load_mqtt_user_config() -> MQTTDefaults:
    """加载用户上次保存的 MQTT 配置。若失败则回退至默认值。"""
    config = _load_user_config()
    
    mqtt_data = config.get("mqtt", {})
    if not mqtt_data:
        return MQTT_DEFAULTS

    try:
        port = int(mqtt_data.get("port", MQTT_DEFAULTS.port))
    except (TypeError, ValueError):
        port = MQTT_DEFAULTS.port

    return MQTTDefaults(
        host=str(mqtt_data.get("host") or MQTT_DEFAULTS.host),
        port=port,
        username=mqtt_data.get("username") or None,
        password=mqtt_data.get("password") or None,
    )


def save_mqtt_user_config(
    *,
    host: str,
    port: int,
    username: str | None,
    password: str | None,
) -> None:
    """将用户在界面中填写的 MQTT 配置持久化到项目根目录。"""
    config = _load_user_config()
    config["mqtt"] = {
        "host": host,
        "port": port,
        "username": username,
        "password": password,
    }
    _save_user_config(config)


def has_db_user_config() -> bool:
    """检查是否存在用户保存的数据库配置。
    
    Returns:
        如果配置文件存在且包含数据库配置，返回 True；否则返回 False
    """
    config = _load_user_config()
    return "database" in config and bool(config["database"])


def load_db_user_config() -> DatabaseDefaults:
    """加载用户上次保存的数据库配置。若失败则回退至默认值。"""
    config = _load_user_config()
    
    db_data = config.get("database", {})
    if not db_data:
        return DB_DEFAULTS

    try:
        port = int(db_data.get("port", DB_DEFAULTS.port))
    except (TypeError, ValueError):
        port = DB_DEFAULTS.port

    return DatabaseDefaults(
        host=str(db_data.get("host") or DB_DEFAULTS.host),
        port=port,
        database=str(db_data.get("database") or DB_DEFAULTS.database),
        user=str(db_data.get("user") or DB_DEFAULTS.user),
        password=str(db_data.get("password") or DB_DEFAULTS.password),
    )


def save_db_user_config(
    *,
    host: str,
    port: int,
    database: str,
    user: str,
    password: str,
) -> None:
    """将用户在界面中填写的数据库配置持久化到项目根目录。"""
    config = _load_user_config()
    config["database"] = {
        "host": host,
        "port": port,
        "database": database,
        "user": user,
        "password": password,
    }
    _save_user_config(config)


def has_topic_template_config() -> bool:
    """检查是否存在用户保存的 Topic 模板配置。
    
    Returns:
        如果配置文件存在且包含Topic模板配置，返回 True；否则返回 False
    """
    config = _load_user_config()
    return "topic_template" in config and bool(config.get("topic_template", {}).get("template"))


def load_topic_template_config() -> TopicTemplateDefaults:
    """加载用户上次保存的 Topic 模板配置。若失败则回退至默认值。"""
    config = _load_user_config()
    
    topic_template_data = config.get("topic_template", {})
    if not topic_template_data or "template" not in topic_template_data:
        return TOPIC_TEMPLATE_DEFAULTS
    
    return TopicTemplateDefaults(
        template=str(topic_template_data.get("template", TOPIC_TEMPLATE_DEFAULTS.template)),
    )


def save_topic_template_config(*, template: str) -> None:
    """将用户在界面中填写的 Topic 模板配置持久化到项目根目录。"""
    config = _load_user_config()
    config["topic_template"] = {
        "template": template,
    }
    _save_user_config(config)


def has_message_template_config() -> bool:
    """检查是否存在用户保存的消息模板配置。
    
    Returns:
        如果配置文件存在且包含消息模板配置，返回 True；否则返回 False
    """
    config = _load_user_config()
    return "message_template" in config and bool(config.get("message_template", {}).get("template"))


def load_message_template_config() -> MessageTemplateDefaults:
    """加载用户上次保存的消息模板配置。若失败则回退至默认值。"""
    config = _load_user_config()
    
    message_template_data = config.get("message_template", {})
    if not message_template_data or "template" not in message_template_data:
        return MESSAGE_TEMPLATE_DEFAULTS
    
    return MessageTemplateDefaults(
        template=str(message_template_data.get("template", MESSAGE_TEMPLATE_DEFAULTS.template)),
    )


def save_message_template_config(*, template: str) -> None:
    """将用户在界面中填写的消息模板配置持久化到项目根目录。"""
    config = _load_user_config()
    config["message_template"] = {
        "template": template,
    }
    _save_user_config(config)


